/**
 * Copyright 2009-2011 xiaonei.com All right reserved.
 * This software is the confidential and proprietary information of Xiaonei.com ("Confidential Information"). 
 * You shall not disclose such Confidential Information and shall use it only in accordance with the terms of the license agreement you entered into with Xiaonei.com.
 * Create by XiaoNeiAntiSpamTeam
 *
 */

package com.xiaonei.testice.util.consistenthash;

import java.util.Collection;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @ClassName: ConsistentHash
 * @Description: 一致性hash
 * @author shaohui.liu shaohui.liu@opi-corp.com
 * @date Mar 16, 2010 3:06:30 PM
 * 
 */
public class ConsistentHash<T> {

    /**
     * hash函数
     */
    private final HashFunction hashFunction;

    /**
     * 虚拟节点个数
     */
    private final int numberOfReplicas;

    /**
     * ring
     */
    private final SortedMap<Integer, T> circle = new TreeMap<Integer, T>();

    public ConsistentHash(HashFunction hashFunction, int numberOfReplicas, Collection<T> nodes) {
        this.hashFunction = hashFunction;
        this.numberOfReplicas = numberOfReplicas;
        for (T node : nodes) {
            add(node);
        }
    }

    /**
     * @Title: add
     * @Description: 增加一个节点
     * @param @param node
     * @return void
     * @throws
     */
    public void add(T node) {
        for (int i = 0; i < numberOfReplicas; i++) {
            circle.put(hashFunction.hash(node.toString() + i), node);
        }
    }

    /**
     * @Title: remove
     * @Description: 删除一个节点
     * @param @param node
     * @return void
     * @throws
     */
    public void remove(T node) {
        for (int i = 0; i < numberOfReplicas; i++) {
            circle.remove(hashFunction.hash(node.toString() + i));
        }
    }

    /**
     * @Title: get
     * @Description: 获取节点
     * @param @param key
     * @param @return
     * @return T
     * @throws
     */
    public T get(Object key) {
        if (circle.isEmpty()) {
            return null;
        }
        int hash = hashFunction.hash(key);
        if (!circle.containsKey(hash)) {
            SortedMap<Integer, T> tailMap = circle.tailMap(hash);
            hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
        }
        return circle.get(hash);
    }
}
